﻿using OpenCvSharp;
using OpenCvSharp.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace 摸鱼
{
    internal static class 扩展
    {
        public static void RemoveAt<T>(this ref Span<T> span, int index)
        {
            span.Slice(index + 1).CopyTo(span.Slice(index));
            span = span.Slice(0, span.Length - 1);
        }
        public static void Add<T>(this ref Span<T> span, T item)
        {
            Span<T> Newspan = new T[span.Length + 1];
            span.CopyTo(Newspan);
            Newspan[^1] = item;
            span = Newspan;
        }
        public static string RemoveDuplicates(this string value)
        {
            Stack<char> stackTemp = new Stack<char>();
            foreach (var item in value)
            {
                if (!stackTemp.Any())
                {
                    stackTemp.Push(item);
                    continue;
                }
                if (stackTemp.Peek() == item)
                {
                    stackTemp.Pop();
                }
                else
                {
                    stackTemp.Push(item);
                }
            }
            return new string(stackTemp.Reverse().ToArray());
        }
        public static Bitmap 去噪二值化(this Mat mat)
        {
            Mat 去噪mat = new Mat();
            int d = 23; //23 像素邻域直径（正奇数）较大的值有助于去掉模糊
            double sigmaColor = 40.0; //40 较大的值有助于模糊像素并去除噪声，控制像素值域的滤波强度
            double sigmaSpace = 500; //500 较大的值有助于增强边缘，但是大到一定程度就失去效果了，控制像素位置域的滤波强度
            Cv2.BilateralFilter(mat.CvtColor(ColorConversionCodes.BGR2GRAY), 去噪mat, d, sigmaColor, sigmaSpace);
            //MeanC表示使用局部区域的平均值减去一个常数C，作为阈值
            //blockSize值越大，图像的二值化结果就越平滑，但同时可能会丢失一些细节, 最大255
            去噪mat = 去噪mat.AdaptiveThreshold(255, AdaptiveThresholdTypes.GaussianC, ThresholdTypes.Binary, blockSize: 155, c: 0.1);
            return 去噪mat.ToBitmap();
        }
        public static Bitmap 去噪(this Mat mat)
        {
            Mat 去噪mat = new Mat();
            int d = 23; //23 像素邻域直径（正奇数）较大的值有助于去掉模糊
            double sigmaColor = 40.0; //40 较大的值有助于模糊像素并去除噪声，控制像素值域的滤波强度
            double sigmaSpace = 500; //500 较大的值有助于增强边缘，但是大到一定程度就失去效果了，控制像素位置域的滤波强度
            Cv2.BilateralFilter(mat.CvtColor(ColorConversionCodes.BGR2GRAY), 去噪mat, d, sigmaColor, sigmaSpace);
            return 去噪mat.ToBitmap();
        }
        public static int GetChannelCount(this PixelFormat pixelFormat)
        {
            switch (pixelFormat)
            {
                case PixelFormat.Format1bppIndexed:
                    return 1;
                case PixelFormat.Format4bppIndexed:
                    return 1; // 虽然4位，但通常作为索引颜色处理
                case PixelFormat.Format8bppIndexed:
                    return 1;
                case PixelFormat.Format16bppGrayScale:
                    return 1; // 灰度图像
                case PixelFormat.Format16bppRgb555:
                    return 3; // 5位红, 5位绿, 5位蓝（1位未用）
                case PixelFormat.Format16bppRgb565:
                    return 3; // 5位红, 6位绿, 5位蓝
                case PixelFormat.Format24bppRgb:
                    return 3; // 8位红, 8位绿, 8位蓝
                case PixelFormat.Format32bppRgb:
                    return 3; // 8位红, 8位绿, 8位蓝, 8位未用
                case PixelFormat.Format32bppArgb:
                    return 4; // 8位红, 8位绿, 8位蓝, 8位Alpha
                case PixelFormat.Format32bppPArgb:
                    return 4; // 预乘Alpha，8位红, 8位绿, 8位蓝, 8位Alpha
                case PixelFormat.Format48bppRgb:
                    return 3; // 16位红, 16位绿, 16位蓝
                case PixelFormat.Format64bppArgb:
                    return 4; // 16位红, 16位绿, 16位蓝, 16位Alpha
                case PixelFormat.Format64bppPArgb:
                    return 4; // 预乘Alpha，16位红, 16位绿, 16位蓝, 16位Alpha
                default:
                    throw new ArgumentException("不支持的像素格式");
            }
        }
        public static double DistanceTo(this OpenCvSharp.Point p1, OpenCvSharp.Point p2)
        {
            return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
        }

    }

    public static class InterruptibleData
    {
        public static InterruptibleDatas<T> NotNull<T>(this T item) where T : class
        {
            InterruptibleDatas<T> _NotNull = new(item is null);
            _NotNull._Source = item;
            return _NotNull;
        }
        public static unsafe InterruptibleDatas<T> NotDefault<T>(this ref T item) where T : struct
        {
            InterruptibleDatas<T> _NotNull = new(item.Equals(default(T)));
            T* ff = (T*)Unsafe.AsPointer<T>(ref item);
            _NotNull.SourceRef = () => ref *ff;
            return _NotNull;
        }
        public static unsafe InterruptibleDatas<T> ToInterruptibleData<T>(this ref T item) where T : struct
        {
            InterruptibleDatas<T> _NotNull = new(false);
            T* ff = (T*)Unsafe.AsPointer<T>(ref item);
            _NotNull.SourceRef = () => ref *ff;
            return _NotNull;
        }
        public sealed class InterruptibleDatas<T> : IInterruptibleData<T>
        {
            public T? _Source;
            public bool isNull { get; set; }
            public IInterruptibleData<T>.SourcePtr<T>? SourceRef;
            public InterruptibleDatas(bool isNull) => this.isNull = isNull;

            private ref T Source
            {
                get
                {
                    if (SourceRef is null)
                        return ref _Source;
                    else
                        return ref SourceRef();
                }
            }
            //----------------Do----------------//
            public IInterruptibleData<T> Do(IInterruptibleData<T>.In action)
            {
                if (!isNull)
                    action(Source);
                return this;
            }
            public IInterruptibleData<T> Do(IInterruptibleData<T>.InRef action)
            {
                if (!isNull)
                    action(ref Source);
                return this;
            }
            //----------------To----------------//
            public IInterruptibleData<Out> To<Out>(IInterruptibleData<T>.InOut<Out> action)
            {
                if (isNull)
                    return new InterruptibleDatas<Out>(true);
                return CreateThis(action(Source));
            }
            public IInterruptibleData<Out> To<Out>(IInterruptibleData<T>.InRefOut<Out> action)
            {
                if (isNull)
                    return new InterruptibleDatas<Out>(true);
                return CreateThis(action(ref Source));
            }
            public IInterruptibleData<Out> To<Out>(IInterruptibleData<T>.InOutRef<Out> action)
            {
                if (isNull)
                    return new InterruptibleDatas<Out>(true);
                return CreateThis(ref action(Source));
            }
            public IInterruptibleData<Out> To<Out>(IInterruptibleData<T>.InRefOutRef<Out> action)
            {
                if (isNull)
                    return new InterruptibleDatas<Out>(true);
                return CreateThis(ref action(ref Source));
            }
            //----------------Where----------------//
            public IInterruptibleData<T> Where(IInterruptibleData<T>.InOutTrue func)
            {
                if (isNull)
                    return new InterruptibleDatas<T>(true);
                if (func(Source))
                    return this;
                else
                    return new InterruptibleDatas<T>(true);
            }
            public DatasEnumerable<Out> GetDatasEnumerable<Out>()
            {
                if (!isNull)
                {
                    T souece = Source;
                    DatasEnumerable<Out> classIEnumerable;
                    if (souece is ICollection<Out> items1)
                        classIEnumerable = new(isNull, items1);
                    else if (souece is IReadOnlyCollection<Out> items2)
                    {
                        List<Out> list = new(items2);
                        classIEnumerable = new(isNull, list);
                    }
                    else if (souece is Out item)
                        classIEnumerable = new(isNull, [item]);
                    else
                        classIEnumerable = new(true, default);
                    return classIEnumerable;
                }
                else
                    return new(true, default);
            }
            public static implicit operator T(InterruptibleDatas<T> value)
            {
                if (!value.isNull)
                    return value.Source;
                else
                    return default;
            }
            public IInterruptibleData<T> NotNull()
            {
                if (Source is null)
                    isNull = true;
                return this;
            }
            public IInterruptibleData<T> NotDefault()
            {
                T souece = Source;
                if (souece is null || souece.Equals(default(T)))
                    isNull = true;
                return this;
            }
            public InterruptibleDatas<Out> CreateThis<Out>(Out item)
            {
                InterruptibleDatas<Out> NewThis = new(false);
                NewThis._Source = item;
                return NewThis;
            }
            public unsafe InterruptibleDatas<Out> CreateThis<Out>(ref Out item)
            {
                InterruptibleDatas<Out> NewThis = new(false);
                Out* ff = (Out*)Unsafe.AsPointer(ref item);
                NewThis.SourceRef = () => ref *ff;
                return NewThis;
            }


            public sealed class DatasEnumerable<Data> : ICollection<Data>, IReadOnlyCollection<Data>
            {
                private Data[] NewItems;
                public int Count => NewItems.Length;
                public bool isNull { get; set; }
                public bool IsReadOnly => false;
                private ICollection<Data> Source;
                public delegate void In(Data item);
                public delegate Out InOut<Out>(Data item);
                public delegate void InRef(ref Data item);
                public delegate bool InOutTrueData(Data item);
                private Dictionary<int, int> SourceAndNewIndex;
                public delegate Out InRefOut<Out>(ref Data item);


                public Data[] Items => NewItems;
                public unsafe DatasEnumerable(bool isNull, ICollection<Data> source)
                {
                    this.isNull = isNull;
                    if (isNull)
                        return;
                    Source = source;
                    NewItems = source.ToArray();
                    int Length = NewItems.Length;
                    SourceAndNewIndex = new();
                    for (int i = 0; i < Length; i++)
                        SourceAndNewIndex[i] = i;
                }
                public DatasEnumerable<Out> ToDatas<Out>(InOut<Out> action)
                {
                    if (isNull)
                        return new(true, default);

                    List<Out> source = new();
                    for (int i = 0, Length = NewItems.Length; i < Length; i++)
                        source.Add(action(NewItems[i]));

                    return new(source.Count == 0, source);
                }
                public DatasEnumerable<Out> ToDatas<Out>(InRefOut<Out> action)
                {
                    if (isNull)
                        return new(true, default);

                    List<Out> source = new();
                    for (int i = 0, Length = NewItems.Length; i < Length; i++)
                        source.Add(action(ref NewItems[i]));

                    return new(source.Count == 0, source);
                }
                public DatasEnumerable<Data> WhereDatas(InOutTrueData func)
                {
                    if (isNull)
                        return this;

                    Span<Data> datas = NewItems;
                    int Length = NewItems.Length;
                    for (int i = Length - 1; i > -1; i--)
                    {
                        if (!func(NewItems[i]))
                        {
                            datas.RemoveAt(i);
                            foreach (var item in SourceAndNewIndex.Keys)
                            {
                                int NewIndex = SourceAndNewIndex[item];
                                if (NewIndex > i)
                                    SourceAndNewIndex[item] = NewIndex - 1;
                                else if (NewIndex == i)
                                    SourceAndNewIndex[item] = -1;
                            }
                        }
                    }
                    NewItems = datas.ToArray();
                    Length = NewItems.Length;
                    return this;
                }
                public DatasEnumerable<Data> DoDatas(InRef action)
                {
                    if (isNull)
                        return this;
                    for (int NewIndex = 0, Length = NewItems.Length; NewIndex < Length; NewIndex++)
                        action(ref NewItems[NewIndex]);
                    return this;
                }
                public DatasEnumerable<Data> DoDatas(In action)
                {
                    if (isNull)
                        return this;
                    for (int NewIndex = 0, Length = NewItems.Length; NewIndex < Length; NewIndex++)
                        action(NewItems[NewIndex]);
                    return this;
                }
                public DatasEnumerable<Data> UpdateSource()
                {
                    if (isNull)
                        return this;
                    try
                    {
                        List<Data> SourceList = new(Source);
                        Source.Clear();
                        int MaxSourceLength = SourceAndNewIndex.Count;
                        for (int sourceIndex = 0; sourceIndex < MaxSourceLength; sourceIndex++)
                        {
                            int NewIndex = SourceAndNewIndex[sourceIndex];
                            if (NewIndex == -1)
                                Source.Add(SourceList[sourceIndex]);
                            else
                                Source.Add(NewItems[NewIndex]);
                        }
                    }
                    catch (System.NotSupportedException)
                    {
                        //是不可更改集合
                    }
                    return this;
                }
                public DatasEnumerable<Data> AddDatas(Data item)
                {
                    Add(item);
                    SourceAndNewIndex[SourceAndNewIndex.Count] = NewItems.Length - 1;
                    return this;
                }
                public DatasEnumerable<Data> AddRangeDatas(Data Default, int length)
                {
                    int Length = NewItems.Length;
                    int maxLength = length + Length;

                    Data[] datas = new Data[maxLength];
                    Span<Data> span = datas;
                    for (int NewIndex = 0; NewIndex < Length; NewIndex++)
                        span[NewIndex] = NewItems[NewIndex];

                    int MaxSourceLength = SourceAndNewIndex.Count;
                    for (int NewIndex = Length, j = 0; NewIndex < maxLength; NewIndex++)
                    {
                        span[NewIndex] = Default;
                        SourceAndNewIndex[MaxSourceLength++] = Length + j++;
                    }
                    NewItems = datas;
                    return this;
                }
                public DatasEnumerable<Data> ClearDatas()
                {
                    Clear();
                    return this;
                }
                public DatasEnumerable<Data> RemoveAtDatas(int index)
                {
                    Span<Data> span = NewItems;
                    span.RemoveAt(index);
                    NewItems = span.ToArray();
                    return this;
                }
                public DatasEnumerable<Data> RemoveDatas(Data item)
                {
                    Remove(item);
                    return this;
                }
                public DatasEnumerable<Data> Print()
                {
                    Debug.WriteLine(string.Join(',', NewItems));
                    return this;
                }

                public void Add(Data item)
                {
                    Span<Data> span = NewItems;
                    span.Add(item);
                    NewItems = span.ToArray();
                }
                public void Clear() => Array.Clear(NewItems);
                public bool Contains(Data item) => NewItems.Contains(item);
                public void CopyTo(Data[] array, int arrayIndex) => NewItems.CopyTo(array, arrayIndex);
                public bool Remove(Data item)
                {
                    int index = Array.FindIndex(NewItems, x => x.Equals(item));
                    if (index != -1)
                    {
                        RemoveAtDatas(index);
                        return true;
                    }
                    return false;
                }
                public IEnumerator<Data> GetEnumerator() => NewItems.AsEnumerable().GetEnumerator();
                IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
            }
        }
        public interface IInterruptibleData<K>
        {
            public bool isNull { get; set; }
            public IInterruptibleData<K> Do(In action);
            public IInterruptibleData<K> Do(InRef action);
            public delegate void In(K item);
            public delegate void InRef(ref K item);

            public IInterruptibleData<Out> To<Out>(InOut<Out> action);
            public IInterruptibleData<Out> To<Out>(InOutRef<Out> action);
            public IInterruptibleData<Out> To<Out>(InRefOut<Out> action);
            public IInterruptibleData<Out> To<Out>(InRefOutRef<Out> action);
            public delegate Out InOut<Out>(K item);
            public delegate Out InRefOut<Out>(ref K item);
            public delegate ref Out InOutRef<Out>(K item);
            public delegate ref Out InRefOutRef<Out>(ref K item);

            public IInterruptibleData<K> NotNull();
            public IInterruptibleData<K> NotDefault();
            public delegate ref Out SourcePtr<Out>();
            public delegate bool InOutTrue(K item);
            public InterruptibleDatas<K>.DatasEnumerable<Out> GetDatasEnumerable<Out>();
        }
    }
}
